using dtPolyRef = System.UInt32;
using dtNodeIndex = System.UInt16;
using System.Runtime.InteropServices;

namespace Recast
{
	public unsafe partial class RecastGlobal
	{
		public unsafe partial class dtNodePool
		{
			public dtNode* m_nodes;
			public dtNodeIndex* m_first;
			public dtNodeIndex* m_next;
			public int m_maxNodes;
			public int m_hashSize;
			public int m_nodeCount;

			public dtNodePool(int maxNodes, int hashSize)
			{
				m_maxNodes = maxNodes;
				m_hashSize = hashSize;

				dtAssert(dtNextPow2((uint)m_hashSize) == (uint)m_hashSize);
				dtAssert(m_maxNodes > 0 && m_maxNodes <= DT_NULL_IDX && m_maxNodes <= (1 << DT_NODE_PARENT_BITS) - 1);
				m_nodes = (dtNode*)dtAlloc(sizeof(dtNode) * m_maxNodes, dtAllocHint.DT_ALLOC_PERM);
				m_next = (dtNodeIndex*)dtAlloc(sizeof(dtNodeIndex) * m_maxNodes, dtAllocHint.DT_ALLOC_PERM);
				m_first = (dtNodeIndex*)dtAlloc(sizeof(dtNodeIndex) * hashSize, dtAllocHint.DT_ALLOC_PERM);
				dtAssert(m_nodes);
				dtAssert(m_next);
				dtAssert(m_first);
				memset(m_first, 0xff, sizeof(dtNodeIndex) * m_hashSize);
				memset(m_next, 0xff, sizeof(dtNodeIndex) * m_maxNodes);
			}
			~dtNodePool()
			{
				dtFree(m_nodes);
				dtFree(m_next);
				dtFree(m_first);
			}
			public void clear()
			{
				memset(m_first, 0xff, sizeof(dtNodeIndex) * m_hashSize);
				m_nodeCount = 0;
			}
			public dtNode* findNode(dtPolyRef id, byte state)
			{
				uint bucket = (uint)(dtHashRef(id) & (m_hashSize - 1));
				dtNodeIndex i = m_first[bucket];
				while (i != DT_NULL_IDX)
				{
					if (m_nodes[i].id == id && m_nodes[i].state == state)
						return &m_nodes[i];
					i = m_next[i];
				}
				return (dtNode*)0;
			}
			public uint findNodes(dtPolyRef id, dtNode* nodes, int maxNodes)
			{
				int n = 0;
				uint bucket = (uint)(dtHashRef(id) & (m_hashSize - 1));
				dtNodeIndex i = m_first[bucket];
				while (i != DT_NULL_IDX)
				{
					if (m_nodes[i].id == id)
					{
						if (n >= maxNodes)
							return (uint)n;
						nodes[n++] = m_nodes[i];
					}
					i = m_next[i];
				}
				return (uint)n;
			}
			public uint getNodeIdx(dtNode* node)
			{
				if (null == node) return 0;
				return (uint)(node - m_nodes) + 1;
			}
			public dtNode* getNodeAtIdx(uint idx)
			{
				if (0 == idx) return (dtNode*)0;
				return &m_nodes[idx - 1];
			}
			public int getMemUsed()
			{
				return Marshal.SizeOf(this) +
				sizeof(dtNode) * m_maxNodes +
				sizeof(dtNodeIndex) * m_maxNodes +
				sizeof(dtNodeIndex) * m_hashSize;
			}
			public int getMaxNodes()
			{
				return m_maxNodes;
			}
			public int getHashSize()
			{
				return m_hashSize;
			}
			public dtNodeIndex getFirst(int bucket)
			{
				return m_first[bucket];
			}
			public dtNodeIndex getNext(int i)
			{
				return m_next[i];
			}
			public int getNodeCount()
			{
				return m_nodeCount;
			}
			//public int dtNodePool(ref dtNodePool @dtNodePool)
			//{ return m_nodeCount; }
			//            public dtNodePool& operator=(ref dtNodePool @dtNodePool) 
			//{ return m_nodeCount;}
			public dtNode* getNode(dtPolyRef id, byte state = 0)
			{
				uint bucket = (uint)(dtHashRef(id) & (m_hashSize - 1));
				dtNodeIndex i = m_first[bucket];
				dtNode* node = null;
				while (i != DT_NULL_IDX)
				{
					if (m_nodes[i].id == id && m_nodes[i].state == state)
						return &m_nodes[i];
					i = m_next[i];
				}
				if (m_nodeCount >= m_maxNodes)
					return null;
				i = (dtNodeIndex)m_nodeCount;
				m_nodeCount++;
				node = &m_nodes[i];
				node->pidx = 0;
				node->cost = 0;
				node->total = 0;
				node->id = id;
				node->state = state;
				node->flags = 0;
				m_next[i] = m_first[bucket];
				m_first[bucket] = i;
				return node;
			}
		}
	}
}
